fix: N+1 query on GET /api/users list endpoint [#9761]#9762
Open
oallw wants to merge 1 commit into
Open
Conversation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
👷 Deploy request for label-studio-docs-new-theme pending review.Visit the deploys page to approve it
|
👷 Deploy request for heartex-docs pending review.Visit the deploys page to approve it
|
✅ Deploy Preview for label-studio-playground canceled.
|
✅ Deploy Preview for label-studio-storybook canceled.
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fix for issue N+1 query on GET /api/users list endpoint #9761
GET /api/userssuffers from N+1 queries. For each user returned,BaseUserSerializertriggers additional queries to resolve:active_organization(FK on User) - 1 query per useractive_organization.created_by(FK on Organization) - 1 query per userom_through(reverse FK, OrganizationMember) - 1 query per userFor an org with 151 users, this produces 605 queries and ~1.5s response time. The query count grows linearly with the number of users.
This is the same pattern that was fixed for the organization members endpoint in PR #7461.
Fix
Add
select_relatedandprefetch_relatedtoUserAPI.get_queryset():This reduces the query count to a constant 3 queries regardless of user count:
Reproduction
Acceptance Criteria
GET /api/users, the number of SQL queries is constant regardless of how many users are in the organization.GET /api/users, theactive_organization_metafield is correctly populated for each user.GET /api/usersand a member has been soft-deleted, that user appears as "Deleted User".GET /api/users/<id>, the response is unchanged.Tests
Added
label_studio/users/tests/test_users_api.pywith 4 test cases:test_list_query_count_does_not_scale_with_user_counttest_list_returns_active_organization_metaactive_organization_metacontainstitleandemailfor all userstest_list_deleted_member_shown_as_deletedtest_retrieve_still_worksAll tests pass on both sqlite and postgresql (tested locally against postgres:16-alpine).
Note on tavern: Contributing guidelines recommend tavern for API endpoint tests. We used Django
TestCasewithassertNumQueriesinstead because the core assertion — that query count is constant regardless of user count — cannot be expressed in tavern. The tests still exercise the full DRF request/response cycle viaAPIClient.Risks
Low risk. The change is a single queryset optimization — no new models, migrations, endpoints, or behavioral changes.
select_related/prefetch_relatedare standard Django ORM and database-agnostic. They change how queries are batched, not what data is returned.get_queryset()is shared by allUserAPIactions (list, retrieve, create, update, delete). The extra JOINs and prefetch add negligible overhead for single-object operations (retrieve, update, delete) and are a net improvement for list.